#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <signal.h>

#include "adns.h"
#include "server.h"
#include "main.h"
#include "timeout.h"

#define INTERVAL 3000000

int current_time;
fd_set readmask, writemask;
static struct timeval tv = {INTERVAL/1000000, INTERVAL%1000000};
static int flag;

int get_current_time()
{
	return (int) time(0l);
}

static void sigalrm_handler(int sig)
{
	flag = 1;
}

static void timeout_callback(void *arg)
{
	printf("call timeout %d\n", *((int *) arg));
}

static void init()
{
	current_time = get_current_time();

	srand(current_time);

	signal(SIGALRM, sigalrm_handler);

	ualarm(INTERVAL, 0);
}

static const char *
ntoa(uint32_t address)
{
	static char buf[32];
	uint32_t a = ntohl(address);
	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
                      (int)(uint8_t)((a>>24)&0xff),
                      (int)(uint8_t)((a>>16)&0xff),
                      (int)(uint8_t)((a>>8 )&0xff),
  		      (int)(uint8_t)((a    )&0xff));
	return buf;
}

static void
main_callback(int result, char type, int count, int ttl,
			  void *addrs, void *orig) {
	char *n = (char*)orig;
	int i;
	for (i = 0; i < count; ++i) {
		if (type == DNS_IPv4_A) {
			printf("%s: %s\n", n, ntoa(((uint32_t*)addrs)[i]));
		} else if (type == DNS_PTR) {
			printf("%s: %s\n", n, ((char**)addrs)[i]);
		}
	}
	if (!count) {
		printf("%s: No answer (%d)\n", n, result);
	}
	fflush(stdout);
}

static void
evdns_server_callback(struct evdns_server_request *req, void *data)
{
	int i, r;
	(void)data;
	// dummy; give 255.255.255.255 as an answer for all A questions,
	// give www.163.com as an answer for all PTR questions.
	for (i = 0; i < req->nquestions; ++i) {
		uint32_t ans = htonl(0xffffffffUL);
		if (req->questions[i]->type == TYPE_A &&
			req->questions[i]->dns_question_class == CLASS_INET) {
			printf(" -- replying for %s (A)\n", req->questions[i]->name);
			r = evdns_server_request_add_a_reply(req, req->questions[i]->name,
										  1, &ans, 10);
			if (r<0)
				printf("eeep, didn't work.\n");
		} else if (req->questions[i]->type == TYPE_PTR &&
				   req->questions[i]->dns_question_class == CLASS_INET) {
			printf(" -- replying for %s (PTR)\n", req->questions[i]->name);
			r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name,
											"www.163.com", 10);
		} else {
			printf(" -- skipping %s [%d %d]\n", req->questions[i]->name,
				   req->questions[i]->type, req->questions[i]->dns_question_class);
		}
	}

	r = evdns_server_request_respond(req, 0);
	if (r<0)
		printf("eeek, couldn't send reply.\n");
}

static void
logfn(int is_warn, const char *msg) {
  (void) is_warn;
  fprintf(stderr, "%s\n", msg);
}

int
main(int c, char **v) {
	int nb;
	int idx;
	int reverse = 0, verbose = 1, servertest = 0;
	struct evdns_server_port *server = 0;

	if (c<2) {
		fprintf(stderr, "syntax: %s [-x] [-v] [-s] hostname\n", v[0]);
		return 1;
	}
	idx = 1;
	while (idx < c && v[idx][0] == '-') {
		if (!strcmp(v[idx], "-x"))
			reverse = 1;
		else if (!strcmp(v[idx], "-v"))
			verbose = 1;
		else if (!strcmp(v[idx], "-s"))
			servertest = 1;
		else
			fprintf(stderr, "Unknown option %s\n", v[idx]);
		++idx;
	}

	init();

	if (verbose)
		evdns_set_log_fn(logfn);

	if (servertest) {
		int sock;
		struct sockaddr_in my_addr;
		sock = socket(PF_INET, SOCK_DGRAM, 0);
		if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
			perror("fcntl");
			exit(1);
		}
		my_addr.sin_family = AF_INET;
		my_addr.sin_port = htons(10053);
		my_addr.sin_addr.s_addr = INADDR_ANY;
		if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) {
			perror("bind");
			exit(1);
		}
		server = evdns_add_server_port(sock, 0, evdns_server_callback, NULL);

		evdns_nameserver_ip_add("127.0.0.1:10053");
	} else {
		evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
	}

	for (; idx < c; ++idx) {
		if (reverse) {
			struct in_addr addr;
			if (!inet_aton(v[idx], &addr)) {
				fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
				continue;
			}
			fprintf(stderr, "resolving %s...\n",v[idx]);
			evdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
		} else {
			fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
			evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
		}
	}

	fflush(stdout);

	for (;;) {
		FD_ZERO(&readmask);
		FD_ZERO(&writemask);

		if (server) {
			server_make_selectmasks(server);
		}

		evdns_make_selectmasks();

		nb = select(FD_SETSIZE, &readmask, &writemask, (fd_set *) 0, &tv); 

		if (server) {
			server_handle_select(server);
		}

		evdns_handle_select();

		if (flag) {
			flag = 0;
			ualarm(INTERVAL, 0);

			run_timeout();
		}
	}

	exit(0);
}
